Õppige, kuidas TestClienti abil oma FastAPI rakendusi tõhusalt testida. Hõlmab parimaid tavasid, täiustatud tehnikaid ja reaalseid näiteid robustsete ja usaldusväärsete API-de loomiseks.
FastAPI testimise meisterklass: põhjalik juhend TestClienti kasutamiseks
FastAPI on kujunenud juhtivaks raamistikuks suure jõudlusega API-de ehitamisel Pythoniga. Selle kiirus, kasutusmugavus ja automaatne andmete valideerimine teevad sellest arendajate lemmiku üle maailma. Kuid hästi ehitatud API on ainult nii hea kui selle testid. Põhjalik testimine tagab, et teie API toimib ootuspäraselt, jääb surve all stabiilseks ja seda saab enesekindlalt tootmiskeskkonda paigaldada. See põhjalik juhend keskendub FastAPI TestClient'i kasutamisele teie API lõpp-punktide tõhusaks testimiseks.
Miks on testimine FastAPI rakenduste jaoks oluline?
Testimine on tarkvaraarenduse elutsĂĽkli ĂĽlioluline samm. See aitab teil:
- Varajane vigade tuvastamine: Püüdke vead kinni enne, kui need jõuavad tootmiskeskkonda, säästes aega ja ressursse.
- Tagada koodi kvaliteet: Edendada hästi struktureeritud ja hooldatavat koodi.
- Vältida regressioone: Garanteerida, et uued muudatused ei riku olemasolevat funktsionaalsust.
- Parandada API usaldusväärsust: Luua kindlustunnet API stabiilsuse ja jõudluse osas.
- Hõlbustada koostööd: Pakkuda teistele arendajatele selget dokumentatsiooni oodatava käitumise kohta.
Sissejuhatus FastAPI TestClienti
FastAPI pakub sisseehitatud TestClient'i, mis lihtsustab teie API lõpp-punktide testimise protsessi. TestClient toimib kerge kliendina, mis suudab saata päringuid teie API-le ilma täisväärtuslikku serverit käivitamata. See muudab testimise oluliselt kiiremaks ja mugavamaks.
TestClienti põhifunktsioonid:
- Simuleerib HTTP-päringuid: Võimaldab saata teie API-le GET, POST, PUT, DELETE ja muid HTTP-päringuid.
- Käsitleb andmete serialiseerimist: Serialiseerib automaatselt päringuandmed (nt JSON-koormad) ja deserialiseerib vastuseandmed.
- Pakub väidete meetodeid: Pakub mugavaid meetodeid olekukoodi, päiste ja vastuste sisu kontrollimiseks.
- Toetab asünkroonset testimist: Töötab sujuvalt FastAPI asünkroonse olemusega.
- Integreerub testimisraamistikega: Integreerub hõlpsalt populaarsete Pythoni testimisraamistikega nagu pytest ja unittest.
Testimiskeskkonna seadistamine
Enne testimise alustamist peate seadistama oma testimiskeskkonna. See hõlmab tavaliselt vajalike sõltuvuste installimist ja testimisraamistiku konfigureerimist.
Paigaldamine
Esiteks veenduge, et teil on installitud FastAPI ja pytest. Saate need installida, kasutades pip-i:
pip install fastapi pytest httpx
httpx on HTTP-klient, mida FastAPI kasutab kapoti all. Kuigi TestClient on osa FastAPI-st, tagab ka httpx'i installimine sujuva testimise. Mõned õpetused mainivad ka requests'i, kuid httpx on rohkem kooskõlas FastAPI asünkroonse olemusega.
Näidisrakendus FastAPI-ga
Loome lihtsa FastAPI rakenduse, mida saame testimiseks kasutada:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.get("/")
async def read_root():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.post("/items/")
async def create_item(item: Item):
return item
Salvestage see kood nimega main.py. See rakendus defineerib kolm lõpp-punkti:
/: Lihtne GET lõpp-punkt, mis tagastab "Hello World" sõnumi./items/{item_id}: GET lõpp-punkt, mis tagastab eseme selle ID alusel./items/: POST lõpp-punkt, mis loob uue eseme.
Esimese testi kirjutamine
NĂĽĂĽd, kui teil on FastAPI rakendus, saate alustada testide kirjutamist, kasutades TestClient'i. Looge samasse kausta, kus asub main.py, uus fail nimega test_main.py.
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
Selles testis:
- Impordime
TestClient'i ja FastAPIapp'i instantsi. - Loome
TestClient'i instantsi, andes sellele etteapp'i. - Defineerime testfunktsiooni
test_read_root. - Testfunktsiooni sees kasutame
client.get("/"), et saata GET-päring juurlõpp-punkti. - Me väidame, et vastuse olekukood on 200 (OK).
- Me väidame, et vastuse JSON on võrdne
{"message": "Hello World"}.
Testide käivitamine pytestiga
Testide käivitamiseks avage lihtsalt terminal kaustas, mis sisaldab teie test_main.py faili, ja käivitage järgmine käsk:
pytest
pytest avastab ja käivitab automaatselt kõik teie projekti testid. Peaksite nägema sarnast väljundit:
============================= test session starts ==============================
platform darwin -- Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
collected 1 item
test_main.py .
============================== 1 passed in 0.01s ===============================
Erinevate HTTP-meetodite testimine
TestClient toetab kõiki standardseid HTTP-meetodeid, sealhulgas GET, POST, PUT, DELETE ja PATCH. Vaatame, kuidas igaüht neist meetoditest testida.
GET-päringute testimine
Nägime juba eelmises jaotises näidet GET-päringu testimisest. Siin on veel üks näide, mis testib lõpp-punkti /items/{item_id}:
def test_read_item():
response = client.get("/items/1?q=test")
assert response.status_code == 200
assert response.json() == {"item_id": 1, "q": "test"}
See test saadab GET-päringu aadressile /items/1 koos päringuparameetriga q=test. Seejärel väidab see, et vastuse olekukood on 200 ja et vastuse JSON sisaldab oodatud andmeid.
POST-päringute testimine
POST-päringu testimiseks peate saatma andmeid päringu kehas. TestClient serialiseerib andmed automaatselt JSON-iks.
def test_create_item():
item_data = {"name": "Example Item", "description": "A test item", "price": 9.99, "tax": 1.00}
response = client.post("/items/", json=item_data)
assert response.status_code == 200
assert response.json() == item_data
Selles testis:
- Loome sõnastiku
item_data, mis sisaldab uue eseme andmeid. - Kasutame
client.post("/items/", json=item_data), et saata POST-päring lõpp-punkti/items/, edastadesitem_dataJSON-koormana. - Väidame, et vastuse olekukood on 200 ja et vastuse JSON vastab
item_data-le.
PUT-, DELETE- ja PATCH-päringute testimine
PUT-, DELETE- ja PATCH-päringute testimine sarnaneb POST-päringute testimisega. Kasutate lihtsalt vastavaid meetodeid TestClient'is:
def test_update_item():
item_data = {"name": "Updated Item", "description": "An updated test item", "price": 19.99, "tax": 2.00}
response = client.put("/items/1", json=item_data)
assert response.status_code == 200
# Lisage väited oodatud vastuse kohta
def test_delete_item():
response = client.delete("/items/1")
assert response.status_code == 200
# Lisage väited oodatud vastuse kohta
def test_patch_item():
item_data = {"price": 29.99}
response = client.patch("/items/1", json=item_data)
assert response.status_code == 200
# Lisage väited oodatud vastuse kohta
Ärge unustage lisada väiteid, et kontrollida, kas vastused on ootuspärased.
Täiustatud testimistehnikad
TestClient pakub mitmeid täiustatud funktsioone, mis aitavad teil kirjutada põhjalikumaid ja tõhusamaid teste.
Sõltuvustega testimine
FastAPI sõltuvuste süstimise süsteem võimaldab teil hõlpsalt süstida sõltuvusi oma API lõpp-punktidesse. Testimisel võite soovida need sõltuvused üle kirjutada, et pakkuda mock- või testispetsiifilisi implementatsioone.
Näiteks oletame, et teie rakendus sõltub andmebaasiühendusest. Saate oma testides andmebaasisõltuvuse üle kirjutada, et kasutada mälusisest andmebaasi:
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base, Session
# Database Configuration
DATABASE_URL = "sqlite:///./test.db" # In-memory database for testing
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# Define User Model
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
password = Column(String)
Base.metadata.create_all(bind=engine)
# FastAPI App
app = FastAPI()
# Dependency to get the database session
def get_db():
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
# Endpoint to create a user
@app.post("/users/")
async def create_user(username: str, password: str, db: Session = Depends(get_db)):
db_user = User(username=username, password=password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
from fastapi.testclient import TestClient
from .main import app, get_db, Base, engine, TestingSessionLocal
client = TestClient(app)
# Override the database dependency for testing
def override_get_db():
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
def test_create_user():
# First, ensure the tables are created, which may not happen by default
Base.metadata.create_all(bind=engine) # important: create the tables in the test db
response = client.post("/users/", params={"username": "testuser", "password": "password123"})
assert response.status_code == 200
assert response.json()["username"] == "testuser"
# Clean up the override after the test if needed
app.dependency_overrides = {}
See näide kirjutab üle get_db sõltuvuse testispetsiifilise funktsiooniga, mis tagastab seansi mälusisesele SQLite andmebaasile. Tähtis: Metaandmete loomine tuleb testimisandmebaasi korrektseks toimimiseks selgesõnaliselt käivitada. Tabeli loomata jätmine põhjustab vigasid, mis on seotud puuduvate tabelitega.
AsĂĽnkroonse koodi testimine
FastAPI on ehitatud asĂĽnkroonseks, seega peate sageli testima asĂĽnkroonset koodi. TestClient toetab asĂĽnkroonset testimist sujuvalt.
Asünkroonse lõpp-punkti testimiseks defineerige lihtsalt oma testfunktsioon kui async:
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.get("/async")
async def async_endpoint():
await asyncio.sleep(0.1) # Simulate some async operation
return {"message": "Async Hello"}
import pytest
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
@pytest.mark.asyncio # Needed to be compatible with pytest-asyncio
async def test_async_endpoint():
response = client.get("/async")
assert response.status_code == 200
assert response.json() == {"message": "Async Hello"}
Märkus: Peate installima pytest-asyncio, et kasutada @pytest.mark.asyncio: pip install pytest-asyncio. Samuti peate vanemate pytesti versioonide kasutamisel veenduma, et asyncio.get_event_loop() on konfigureeritud. Kui kasutate pytesti versiooni 8 või uuemat, ei pruugi see vajalik olla.
Failide ĂĽleslaadimise testimine
FastAPI teeb failide üleslaadimise käsitlemise lihtsaks. Failide üleslaadimise testimiseks saate kasutada TestClient'i päringumeetodite files parameetrit.
from fastapi import FastAPI, File, UploadFile
from typing import List
app = FastAPI()
@app.post("/files/")
async def create_files(files: List[bytes] = File()):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile]):
return {"filenames": [file.filename for file in files]}
from fastapi.testclient import TestClient
from .main import app
import io
client = TestClient(app)
def test_create_files():
file_content = b"Test file content"
files = [('files', ('test.txt', io.BytesIO(file_content), 'text/plain'))]
response = client.post("/files/", files=files)
assert response.status_code == 200
assert response.json() == {"file_sizes": [len(file_content)]}
def test_create_upload_files():
file_content = b"Test upload file content"
files = [('files', ('test_upload.txt', io.BytesIO(file_content), 'text/plain'))]
response = client.post("/uploadfiles/", files=files)
assert response.status_code == 200
assert response.json() == {"filenames": ["test_upload.txt"]}
Selles testis loome näidisfaili, kasutades io.BytesIO, ja edastame selle files parameetrile. Parameeter files aktsepteerib ennikute loendit, kus iga ennik sisaldab välja nime, failinime ja faili sisu. Sisu tüüp on oluline serveri täpseks käsitlemiseks.
Veakäsitluse testimine
On oluline testida, kuidas teie API vigadega toime tuleb. Saate kasutada TestClient'i, et saata kehtetuid päringuid ja kontrollida, kas API tagastab õiged veavastused.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id > 100:
raise HTTPException(status_code=400, detail="Item ID too large")
return {"item_id": item_id}
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item_error():
response = client.get("/items/101")
assert response.status_code == 400
assert response.json() == {"detail": "Item ID too large"}
See test saadab GET-päringu aadressile /items/101, mis tõstatab HTTPException'i olekukoodiga 400. Test väidab, et vastuse olekukood on 400 ja et vastuse JSON sisaldab oodatud veateadet.
Turvafunktsioonide testimine
Kui teie API kasutab autentimist või autoriseerimist, peate ka neid turvafunktsioone testima. TestClient võimaldab teil seada päiseid ja küpsiseid, et simuleerida autenditud päringuid.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
app = FastAPI()
# Security
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Simulate authentication
if form_data.username != "testuser" or form_data.password != "password123":
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password")
return {"access_token": "fake_token", "token_type": "bearer"}
@app.get("/protected")
async def protected_route(token: str = Depends(oauth2_scheme)):
return {"message": "Protected data"}
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_login():
response = client.post("/token", data={"username": "testuser", "password": "password123"})
assert response.status_code == 200
assert "access_token" in response.json()
def test_protected_route():
# First, get a token
token_response = client.post("/token", data={"username": "testuser", "password": "password123"})
token = token_response.json()["access_token"]
# Then, use the token to access the protected route
response = client.get("/protected", headers={"Authorization": f"Bearer {token}"}) # corrected format.
assert response.status_code == 200
assert response.json() == {"message": "Protected data"}
Selles näites testime sisselogimise lõpp-punkti ja kasutame seejärel saadud luba kaitstud marsruudile juurdepääsemiseks. TestClient'i päringumeetodite headers parameeter võimaldab teil seada kohandatud päiseid, sealhulgas Authorization päist kandjalubade jaoks.
FastAPI testimise parimad tavad
Siin on mõned parimad tavad, mida järgida oma FastAPI rakenduste testimisel:
- Kirjutage põhjalikke teste: Püüdke saavutada kõrge testide katvus, et tagada kõigi oma API osade põhjalik testimine.
- Kasutage kirjeldavaid testinimesid: Veenduge, et teie testinimed näitaksid selgelt, mida test kontrollib.
- Järgige Arrange-Act-Assert mustrit: Korraldage oma testid kolme eraldiseisvasse faasi: Arrange (testiandmete ettevalmistamine), Act (testitava tegevuse sooritamine) ja Assert (tulemuste kontrollimine).
- Kasutage mock-objekte: Mockige väliseid sõltuvusi, et isoleerida oma teste ja vältida sõltuvust välistest süsteemidest.
- Testige äärmuslikke juhtumeid: Testige oma API-d kehtetu või ootamatu sisendiga, et tagada vigade sujuv käsitlemine.
- Käivitage teste sageli: Integreerige testimine oma arendustöövoogu, et püüda vigu varakult ja sageli.
- Integreerige CI/CD-ga: Automatiseerige oma testid oma CI/CD torujuhtmes, et tagada kõigi koodimuudatuste põhjalik testimine enne tootmiskeskkonda paigaldamist. Selle saavutamiseks saab kasutada tööriistu nagu Jenkins, GitLab CI, GitHub Actions või CircleCI.
Näide: Rahvusvahelistamise (i18n) testimine
Globaalsele publikule API-de arendamisel on rahvusvahelistamine (i18n) hädavajalik. I18n testimine hõlmab kontrollimist, kas teie API toetab korrektselt mitut keelt ja piirkonda. Siin on näide, kuidas saate i18n-i FastAPI rakenduses testida:
from fastapi import FastAPI, Header
from typing import Optional
app = FastAPI()
messages = {
"en": {"greeting": "Hello, world!"},
"fr": {"greeting": "Bonjour le monde !"},
"es": {"greeting": "¡Hola Mundo!"},
}
@app.get("/")
async def read_root(accept_language: Optional[str] = Header(None)):
lang = accept_language[:2] if accept_language else "en"
if lang not in messages:
lang = "en"
return {"message": messages[lang]["greeting"]}
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_root_en():
response = client.get("/", headers={"Accept-Language": "en-US"})
assert response.status_code == 200
assert response.json() == {"message": "Hello, world!"}
def test_read_root_fr():
response = client.get("/", headers={"Accept-Language": "fr-FR"})
assert response.status_code == 200
assert response.json() == {"message": "Bonjour le monde !"}
def test_read_root_es():
response = client.get("/", headers={"Accept-Language": "es-ES"})
assert response.status_code == 200
assert response.json() == {"message": "¡Hola Mundo!"}
def test_read_root_default():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello, world!"}
See näide seab Accept-Language päise soovitud keele määramiseks. API tagastab tervituse määratud keeles. Testimine tagab, et API käsitleb korrektselt erinevaid keele-eelistusi. Kui Accept-Language päis puudub, kasutatakse vaikimisi "en" keelt.
Kokkuvõte
Testimine on robustsete ja usaldusväärsete FastAPI rakenduste ehitamise oluline osa. TestClient pakub lihtsat ja mugavat viisi oma API lõpp-punktide testimiseks. Järgides selles juhendis kirjeldatud parimaid tavasid, saate kirjutada põhjalikke teste, mis tagavad teie API-de kvaliteedi ja stabiilsuse. Alates põhipäringutest kuni täiustatud tehnikateni nagu sõltuvuste süstimine ja asünkroonne testimine, annab TestClient teile võimaluse luua hästi testitud ja hooldatavat koodi. Võtke testimine omaks oma arendustöövoo põhiosana ja te ehitate API-sid, mis on nii võimsad kui ka usaldusväärsed kasutajatele üle maailma. Pidage meeles CI/CD integreerimise tähtsust testimise automatiseerimiseks ja pideva kvaliteedi tagamiseks.